package com.letoken.platform.app.service.transfer;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.letoken.platform.app.service.AppUserWalletService;
import com.letoken.platform.app.service.impl.WithdrawCheckByUserIdServiceImpl;
import com.letoken.platform.pub.constant.ConstantsValue;
import com.letoken.platform.pub.constant.RedisKeyConstant;
import com.letoken.platform.pub.enums.*;
import com.letoken.platform.pub.exception.ApiServiceException;
import com.letoken.platform.repo.admin.dao.AdminCommunityConfigMapper;
import com.letoken.platform.repo.admin.manager.AdminWithdrawConfigManager;
import com.letoken.platform.repo.admin.po.AdminWithdrawConfig;
import com.letoken.platform.repo.admin.po.Withdraw;
import com.letoken.platform.repo.app.dao.AppUserWalletMapper;
import com.letoken.platform.repo.app.dao.AppWithdrawConfigMapper;
import com.letoken.platform.repo.app.manager.AppUserAssetsHistoryManager;
import com.letoken.platform.repo.app.manager.AppUserWalletManager;
import com.letoken.platform.repo.app.po.AppUser;
import com.letoken.platform.repo.app.po.AppUserAssetsHistory;
import com.letoken.platform.repo.app.po.AppUserWallet;
import com.letoken.platform.repo.app.redis.AppWalletBlackListRedis;
import com.letoken.platform.repo.app.redis.RedisConfig;
import com.letoken.platform.repo.app.req.token.WithdrawUsdtReq;
import com.letoken.platform.repo.app.req.user.UserLevelReq;
import com.letoken.platform.repo.utlis.GasUtils;
import com.letoken.platform.repo.utlis.LocalBlockUtils;
import com.letoken.platform.repo.utlis.WebUtils;
import com.letoken.platform.repo.utlis.lock.Lock;
import com.letoken.platform.repo.utlis.lock.LockCategoryEnum;
import com.letoken.platform.repo.web3.util.EvmUtil;
import org.apache.commons.lang3.ObjectUtils;
import org.checkerframework.checker.units.qual.A;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;

@Component
public class WithdrawService extends AbstractTransfer{
    @Resource
    private AdminWithdrawConfigManager withdrawConfigManager;
    @Resource
    private AppUserWalletManager walletManager;
    @Resource
    private AppUserAssetsHistoryManager historyManager;
    @Resource
    private WithdrawCheckByUserIdServiceImpl userIdServiceImpl;
    @Resource
    AppUserWalletMapper appUserWalletMapper;

    @Autowired
    GasUtils gasUtils;

    @Resource
    private RedisConfig redisConfig;



    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public ITransfer checkout(WithdrawUsdtReq req) {
        // check(req.getUser());
//        if (req.getTokenId().equals(LetokenTokenTypeEnum.USDT.getId())){
//            throw new ApiServiceException("系统升级中!");
//        }
        super.checkout(req);
        // todo capa暂不支持外提
        if (req.getTokenId().equals(LetokenTokenTypeEnum.CAPA.getId())){
            throw new ApiServiceException(LetokenApiResponseEnum.ERROR_MALL_SMS_058);
        }
        /*if(!req.getUser().getId().equals(16532533)){
            if (req.getTokenId().equals(LetokenTokenTypeEnum.LP.getId())){
                throw new ApiServiceException(LetokenApiResponseEnum.ERROR_MALL_SMS_058);
            }
        }*/


//        if (req.getTokenId().equals(LetokenTokenTypeEnum.USDT.getId())){
//            throw new ApiServiceException(LetokenApiResponseEnum.ERROR_MALL_SMS_058);
//        }
//        if (req.getTokenId().equals(LetokenTokenTypeEnum.CAP.getId())){
//            throw new ApiServiceException(LetokenApiResponseEnum.ERROR_MALL_SMS_058);
//        }


//        if (req.getTokenId().equals(LetokenTokenTypeEnum.CAP.getId())){
//            Integer userId = WebUtils.getUserId();
//            if (userId.equals(16542439)  ||  userId.equals(16540616) ){
//
//            }else {
//                throw new ApiServiceException(LetokenApiResponseEnum.ERROR_MALL_SMS_058);
//            }
//        }


        // 先查提现配置
        AdminWithdrawConfig withdrawConfig = withdrawConfigManager.queryByToken(req.getToken().getId());
        if (ObjectUtil.isNull(withdrawConfig)) {
            throw new ApiServiceException(LetokenApiResponseEnum.ERROR_WALLET_WITHDRAW_009);
        }
        if (req.getAmount().compareTo(BigDecimal.ONE) <= 0) {
            throw new ApiServiceException(LetokenApiResponseEnum.ERROR_WALLET_WITHDRAW_013);
        }
        // 不能超过配置规定的最小数量和最大数量
        if (req.getAmount().compareTo(new BigDecimal(withdrawConfig.getMinAmount())) < 0) {
            throw new ApiServiceException(LetokenApiResponseEnum.ERROR_WALLET_WITHDRAW_005, "提现金额小于" + withdrawConfig.getMinAmount());
        }
        if (req.getAmount().compareTo(new BigDecimal(withdrawConfig.getMaxAmount())) > 0) {
            throw new ApiServiceException(LetokenApiResponseEnum.ERROR_WALLET_WITHDRAW_006, "提现金额大于" + withdrawConfig.getMaxAmount());
        }
        //提现金额是否大于自己的金额 大于就报错
        AppUserWallet fromAppUserWallet = walletManager.queryWalletByToken(req.getUser().getId(), req.getToken().getId());
        if (req.getAmount().compareTo(fromAppUserWallet.getAvailableBalance()) > 0) {
            throw new ApiServiceException(LetokenApiResponseEnum.ERROR_WALLET_WITHDRAW_007);
        }
        req.setWalletId(fromAppUserWallet.getId());

        LocalBlockUtils.AddressPo po = LocalBlockUtils.obtainUserId(LocalBlockUtils.ObtainUserIdDto.builder()
                .addr(req.getUserAddressOrUserId())
                .coinId(LetokenTokenTypeEnum.USDT.getId())
                .build());

        //链上转账不能转到平台内
        if (po.isSuccess()){
            throw new ApiServiceException(LetokenApiResponseEnum.ERROR_WALLET_WITHDRAW_015);
        }
        //验证地址有效性
        if (!EvmUtil.checkAddressChecksum(req.getUserAddressOrUserId())) {
            throw new ApiServiceException(LetokenApiResponseEnum.ERROR_WALLET_WITHDRAW_010);
        }
        req.setFee(BigDecimal.ZERO);
        if (ObjectUtils.isNotEmpty(withdrawConfig.getFeeAmount())){
            //*req.setFee(withdrawConfig.getFeeAmount());
            // cap 是比例
            /*if (req.getTokenId().equals(LetokenTokenTypeEnum.CAP.getId())){
                req.setFee(req.getAmount().multiply(withdrawConfig.getFeeAmount()));
            }

            BigDecimal fee = req.getAmount().multiply(withdrawConfig.getFeeAmount());
            BigDecimal gasFee = gasUtils.getFee(fee,2);
            if (!req.getTokenId().equals(LetokenTokenTypeEnum.CAP.getId())){
                gasUtils.systemGasFee(req.getUser().getId(),gasFee);
                req.setFee(gasFee);
            }else {
                if(fromAppUserWallet.getAvailableBalance().compareTo(fee.add(req.getAmount()))<0){
                    req.setAmount(req.getAmount().subtract(fee));
                }
                gasUtils.systemGasFee(req.getUser().getId(),fee);
                req.setFee(fee);
            }*/
            req.setFee(req.getAmount().multiply(withdrawConfig.getFeeAmount()));
        }

        req.setAmount(req.getAmount());
        req.setToAddress(req.getUserAddressOrUserId());

        Withdraw insertWithdraw = userIdServiceImpl.withdrawCheckByUserId(req.getUser().getId(), req.getUserAddressOrUserId(), req.getAmount(), req.getToken(), req.getUuId());
        if (insertWithdraw != null) {
            insertWithdraw.insert();
            throw new ApiServiceException(LetokenApiResponseEnum.ERROR_WALLET_WITHDRAW_017);
        }
        return SpringUtil.getBean(WithdrawService.class);
    }



    @Override
    @Lock(ce = LockCategoryEnum.ID,value = "WALLET:MODIFY",field = "walletId",expire = 3l)
    @Transactional(rollbackFor = Exception.class)
    public void transfer(WithdrawUsdtReq req) {
//        if (req.getTokenId().equals(LetokenTokenTypeEnum.USDT.getId())){
//            throw new ApiServiceException("系统升级中!");
//        }
        AppUserWallet fromAppUserWallet = appUserWalletMapper.selectById(req.getWalletId());
        if (req.getAmount().compareTo(fromAppUserWallet.getAvailableBalance()) > 0) {
            throw new ApiServiceException(LetokenApiResponseEnum.ERROR_WALLET_WITHDRAW_007);
        }
       // BigDecimal subAmount = req.getAmount();
        //手续费
        BigDecimal gasFee = gasUtils.getFee(req.getFee(),2);
        if (!req.getTokenId().equals(LetokenTokenTypeEnum.CAP.getId()) && !req.getTokenId().equals(LetokenTokenTypeEnum.LP.getId())){
            gasUtils.systemGasFee(req.getUser().getId(),gasFee);
            req.setFee(gasFee);
        }else  if(req.getTokenId().equals(LetokenTokenTypeEnum.LP.getId())){
            BigDecimal fee = gasUtils.getFee(BigDecimal.ZERO,1);
            gasUtils.systemGasFee(req.getUser().getId(),fee);
            req.setFee(fee);
        }else {
            if(fromAppUserWallet.getAvailableBalance().compareTo(req.getFee().add(req.getAmount()))<=0){
                req.setAmount(req.getAmount().subtract(req.getFee()));
            }
            gasUtils.systemGasFee(req.getUser().getId(),req.getFee());
        }


        fromAppUserWallet = appUserWalletMapper.selectForUpdate(req.getWalletId());
        AppUserWallet auw = new AppUserWallet();
        String key = String.format(RedisKeyConstant.UPDATE_WALLET_INFO,fromAppUserWallet.getUserId());
        RedissonClient redissonClient = redisConfig.getRedisson();
        RLock rlock = redissonClient.getLock(key);
        try {
            auw.setId(fromAppUserWallet.getId());
            auw.setAvailableBalance(fromAppUserWallet.getAvailableBalance().subtract(req.getAmount()));
            auw.setFreezeBalance(fromAppUserWallet.getFreezeBalance().add(req.getAmount()));
            appUserWalletMapper.updateById(auw);
        }finally {
            if (rlock != null && rlock.isHeldByCurrentThread()) {
                rlock.unlock();
            }
        }


        AppUserAssetsHistory appUserAssetsHistory = new AppUserAssetsHistory();
        appUserAssetsHistory.setUserId(req.getUser().getId());
        appUserAssetsHistory.setType(TokenTransactionTypeEnum.WITHDRAW.getValue());
        appUserAssetsHistory.setStatus(TokenTransactionStatusEnum.PENDING.getValue());
        appUserAssetsHistory.setTokenId(req.getToken().getId());
        appUserAssetsHistory.setTokenCurrency(req.getToken().getTokenCurrency());
        appUserAssetsHistory.setAmount(req.getAmount().negate());
        appUserAssetsHistory.setSn(req.getUuId());
        appUserAssetsHistory.setFromUserName(req.getUser().getUserName());
        appUserAssetsHistory.setFromAddress(req.getFromAddress());
        appUserAssetsHistory.setToAddress(req.getToAddress());
        appUserAssetsHistory.setCreateTime(req.getNow());
        appUserAssetsHistory.setUpdateTime(req.getNow());
        appUserAssetsHistory.setBalance(auw.getAvailableBalance());
        appUserAssetsHistory.insert();



        Withdraw withdraw = new Withdraw();
        withdraw.setSn(req.getUuId());
        withdraw.setUserId(req.getUser().getId());
        withdraw.setTokenId(req.getToken().getId());
        withdraw.setChainId(req.getToken().getChainId());
        //收款地址
        withdraw.setAddress(req.getUserAddressOrUserId());
        //没有收款id
        withdraw.setToId(null);
        withdraw.setAmount(req.getAmount());
        //手续费
        withdraw.setFeeAmount(BigDecimal.ZERO);
        // 平台互转 transfer_mode 为0 反之为1
        withdraw.setTransferMode(req.getTransferMode());
        withdraw.setTxid("");
        withdraw.setStatus(TokenTransactionStatusEnum.PENDING.getValue());
        withdraw.setCreateTime(req.getNow());
        withdraw.setApprovedDate(req.getNow());
        withdraw.setResponse("");
        withdraw.setRemark("");
        withdraw.setApprovedState(TokenApprovedStateEnum.PENDING.getValue());
        withdraw.setIsAuto(false);
        withdraw.setFeeAmount(req.getFee());
        withdraw.setRealAmount(withdraw.getAmount());
        withdraw.insert();

        // todo 申请提币
        LocalBlockUtils.withdrawApply(LocalBlockUtils.WithDrawApplyDto.builder()
                .seqNo(req.getUuId())
                .remark("用户申请")
                .amount(req.getAmount())
                .coinId(req.getToken().getId() == 2 ? 1 : req.getToken().getId())
                .toAddr(req.getUserAddressOrUserId())
                .userId(req.getUser().getId())
                .build());
    }

    @Override
    public ITransfer choose(Integer mode) {
        if (ConstantsValue.I_1.equals(mode))
            return this;
        else
            return null;
    }


}
